﻿using System;
using System.Linq;
using System.Reflection;
using System.Diagnostics;
using System.Collections.Generic;

using SimbaRGVMonitor.Common.Linq;

namespace SimbaRGVMonitor.Common.Diagnostics
{
    static class ExceptionTrace
    {
        public static StackTraceMessage[] GetExceptionMessages(string message, Exception ex)
        {
            return GetExceptionMessagesCore(message, ex, 2);
        }

        //====================================================================================================
        //以上是读取操作
        //----------------------------------------------------------------------------------------------------
        //以下是分析消息和异常
        //====================================================================================================

        private static StackTraceMessage[] GetExceptionMessagesCore(string message, Exception ex, int skipFrames)
        {
            var collection = new List<StackTraceMessage>();

            //当前位置的堆栈信息
            {
                var trace = new StackTrace(skipFrames, true);
                var wouldAdd = GenerateStackTraceMessage("Message", message, trace);

                collection.Add(wouldAdd);
            }

            //异常追踪
            while (ex != null)
            {
                var trace = new StackTrace(ex, true);
                var wouldAdd = GenerateStackTraceMessage(ex.GetType().Name, ex.Message, trace);

                collection.Add(wouldAdd);
                ex = ex.InnerException;
            }

            //返回
            return collection.ToArray();
        }

        private static StackTraceMessage GenerateStackTraceMessage(string typeFlag, string title, StackTrace trace)
        {
            var flagFormat = string.Format("{0}: ", typeFlag);
            var newTitle = flagFormat + title;

            return new StackTraceMessage(newTitle, CollectStackMessage(trace));
        }

        //====================================================================================================
        //以上是分析消息和异常
        //----------------------------------------------------------------------------------------------------
        //以下是收集堆栈信息
        //====================================================================================================

        public static string CollectStackMessage(StackTrace stackTrace)
        {
            var result = string.Empty;
            var frames = stackTrace.GetFrames();
            if (frames != null)
            {
                frames.ForEach(
                    thisFrame =>
                    {
                        var method = thisFrame.GetMethod();
                        var methodName = GetMethodName(method);
                        var fileNameAndLineNumber = CollectFileNameAndLineNumber(thisFrame);

                        result += string.Format("{0}{1}", methodName, fileNameAndLineNumber);
                        result += Environment.NewLine;
                    });
            }
            return result;
        }

        public static string GetMethodName(MethodBase methodBase)
        {
            var methodName = methodBase.Name;
            var inputParams = CollectInputParams(methodBase);
            var typeName = methodBase.DeclaringType == null ? string.Empty : methodBase.DeclaringType.FullName;
            return string.Format("{0}.{1}({2})", typeName, methodName, inputParams);
        }

        private static string CollectInputParams(MethodBase methodBase)
        {
            return methodBase
                .GetParameters()
                .Select(info => string.Format("{0} {1}", info.ParameterType.Name, info.Name))
                .ConcatText(", ");
        }

        private static string CollectFileNameAndLineNumber(StackFrame thisFrame)
        {
            var result = string.Empty;
            if (thisFrame.GetFileName() != null)
            {
                var fileName = thisFrame.GetFileName();
                var lineNumber = thisFrame.GetFileLineNumber();
                result = string.Format(" in {0}:line {1}", fileName, lineNumber);
            }
            return result;
        }
    }
}
